Skip to content

Add native delivery for Codex and Claude - #254

Merged
myobie merged 56 commits into
mainfrom
agent/native-delivery-one
Aug 16, 2026
Merged

Add native delivery for Codex and Claude#254
myobie merged 56 commits into
mainfrom
agent/native-delivery-one

Conversation

@myobie

@myobie myobie commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Nathan asked for the whole native delivery change in one pull request. This is it. It replaces #241, which is now closed and points here.

What this contains

Both harnesses, in one branch, on top of current main.

Codex gets a declared app-server delivery. st2 starts the Codex process through st2 codex-app-server, binds the thread, and writes messages into the durable inbox.

Claude gets an MCP inbox watcher that Claude owns. Claude spawns it as its own child and it dies with the session.

The last three commits are the Claude half and the presence fix:

  • 5ad1a99 Add Claude-owned MCP inbox watcher
  • 23e68da Clarify Codex native ownership boundary
  • b184715 Refresh Codex native presence from session loop

The specification stays separate in #242. #232 also stays separate, because it is a decision and not a patch.

The architecture, stated plainly

st2 writes a file to the inbox. That is all st2 does. A watcher observes the folder and sends into the provider's channel. The agent acts and archives, and the archive is the settlement.

st2 does not call the channel directly, for either harness. There is no cursor, no lease, no claim and no dedup table. A restart is just another start: it reads the folder, and anything still there is outstanding by definition.

Opt-in

Nothing changes for an agent that does not declare it. Existing agents keep the ding until their declaration says otherwise.

macOS: this branch is at parity with main, and it fixes a real Darwin defect

Every test on this branch passes on macOS except one, and that one fails on main too.

Measured on Darwin 25.6.0, Apple Silicon, full library suite:

this branch, 8 runs at default parallelism : 1 failure, always ding::deferred_delivery_backoff
plain main,  3 runs at default parallelism : 1 failure, the same test
this branch, 3 runs at --test-threads=2    : the same one failure

That remaining test is not ours. It fails on main with none of this work in the tree, and it is
filed separately as #255.

A real macOS delivery defect was found and fixed here

When a Codex app-server wrote its messages and exited promptly, macOS threw those messages away.

Darwin fails setsockopt as soon as the peer is gone, so the control pump abandoned the connection
with data still buffered and unread. Linux delivers buffered bytes after a close, so the pump read
on and bound normally — which is why CI, which is ubuntu-latest only, never saw it.

The fix drains what already arrived before reporting the connection closed. A close is an
end-of-stream marker, not a stop signal. It took the affected test from 7 failures in 8 runs to 0
in 8
.

Two fixture budgets were also raised, because they were sized for a quieter machine and timed out
under parallel Darwin scheduling. Both changes are inside mod tests.

How it was found

CI proves Linux only. Every failure here came from running the suite on a Mac, and each fix was
measured over matched samples of six to eight full runs before it was believed. Two of the commits
are diagnostics rather than fixes, and they are the reason the rest exist — three theories died
against them.

@myobie

myobie commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Rollout finding: the old 6d Codex wrapper left its app-server process group alive after the owning PTY daemon stopped. The orphan stayed invisible until the 9f wrapper tried to replace it and correctly refused a second live control owner. A verified TERM of the exact old process group released the socket, and the 9f driver then recovered with a real bus round trip. A later doctor improvement should report a live Codex app-server group or socket that no current wrapper owns. This follow-up is recorded only; this PR does not add that doctor check.

@myobie

myobie commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Rollout follow-up: the three hosts do not have one sanctioned binary transport. Droppy cannot build Rust. Pulling from Hetz failed because Droppy lacked inbound SSH authorization. A Hetz-to-Droppy push over Tailscale SSH worked, but it depended on a machine key. The synced catalog is not artifact storage and must not be used as a workaround. The fleet needs an owned artifact transport with integrity verification.

myobie commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

macOS comparison against the PR base:

  • Base 6d1d9d2: 761 passed, 13 failed across 9 distinct tests.
  • Head a5ea782: 775 passed, 11 failed across 7 distinct tests.

PR #254 fixes these three tests on macOS:

  • marker_time_message_write_remains_bound_to_its_retained_agent_capability
  • marker_time_state_routes_existing_orphans_but_never_flat_falls_back_for_new_agents
  • state_remains_addressable_after_its_spec_is_deleted_mid_apply

completed_catalog_aba_during_runtime_observation_is_incomplete failed once during the head full-suite run. It passed five isolated runs at both commits. This result indicates a load-sensitive flake, not a PR regression.

The remaining macOS failures exist on the base. classification_only_and_nested_agent_filename_changes_are_exact is tracked by issue #183. Issue #255 records that the main branch does not pass its full Darwin suite.

Both full suites used --no-fail-fast. This comparison corrects an earlier fail-fast report that exposed only the first failing target.

myobie added 24 commits August 15, 2026 10:22
Proven on a real Claude session: the rendered MCP declaration caused Claude to spawn the watcher as its child; killing Claude removed the watcher, with no st2 task or DING sidecar. A self-authored distinctive token reached the model, which replied with the exact body token and archived the original. The first token run was blocked by a nonexistent scratch sender identity, a test-rig defect rather than a transport defect.

The watcher refreshes native presence in its session loop. Known limit: if Claude hangs without closing stdio, the watcher remains attached to that hung session; no external heartbeat or supervisor is added.
@myobie
myobie force-pushed the agent/native-delivery-one branch from a5ea782 to 3687a0b Compare August 15, 2026 08:23
@myobie
myobie merged commit 86cce30 into main Aug 16, 2026
1 check passed
@myobie
myobie deleted the agent/native-delivery-one branch August 16, 2026 10:10
@schickling schickling added area:ding DING delivery: inbox notice into a running agent · Set: manual area:driver Harness drivers: launch, MCP, app-server, native delivery · Set: manual harness:claude Claude Code-specific behavior · Set: manual harness:codex Codex-specific behavior · Set: manual origin:agent Filed or primarily produced by an AI agent · Set: manual type:feature New user-visible or system capability · Set: manual labels Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ding DING delivery: inbox notice into a running agent · Set: manual area:driver Harness drivers: launch, MCP, app-server, native delivery · Set: manual harness:claude Claude Code-specific behavior · Set: manual harness:codex Codex-specific behavior · Set: manual origin:agent Filed or primarily produced by an AI agent · Set: manual type:feature New user-visible or system capability · Set: manual

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants